var MongoClient = require('mongodb').MongoClient;
var ObjectID = require('mongodb').ObjectID;

var connectionString = process.env.MONGOLAB_DB ? process.env.MONGOLAB_DB : 'mongodb://admin:mta2013@ds039078.mongolab.com:39078/activiteam';

//returns: _id on success, error on failure
exports.createEvent = function(req,res) {
    var _db = null;
	
	function onMongoConnect(err, db) {
		if(err) {
			console.warn(err);
			res.json({ 'error' : err });
		}
		else {
			_db = db;
			var newEvent = parseReq(req);
			db.collection('events').insert(newEvent, onCollectionInsert);
		}
	}
	
	function parseReq(req) {
		var newEvent = {};
		var fields = ['name', 'sportType', 'skillLevel', 'startTime', 'endTime',
            'minParticipants', 'maxParticipants', 'location', 'createdBy', 'members'];
		for (var i = 0; i < fields.length; i++) {
			if (typeof(req.body[fields[i]]) != 'undefined') {
				if (fields[i] == 'startTime' || fields[i] == 'endTime') {
					newEvent[fields[i]] = new Date(req.body[fields[i]]);
				}
				else {
					newEvent[fields[i]] = req.body[fields[i]];
				}
			}
		}
		
		return newEvent;
	}
	
	function onCollectionInsert(err, objects) {
		var resJson = null;
		
		if (err) {
			console.warn(err);
			resJson = { 'error' : err };
		}
		else {
			resJson = { '_id' : objects[0]._id.toString() };
		}
		
		_db.close();
		
		res.json(resJson);
	}	
	
	MongoClient.connect(connectionString, onMongoConnect);
};

//returns: all events on success, error on failure
exports.getEvents = function(req,res){
    var _db = null;
	
	function onMongoConnect(err, db) {
		if(err) {
			console.warn(err);
			res.json({ 'error' : err });
		}
		else {
			_db = db;
			var findCriteria = createCriteria(req.query);
			db.collection('events').find(findCriteria).toArray(onCollectionFind);
		}
	}
	
	function createCriteria(queryString) {
		var criterias = [];
		
		if(typeof(queryString.sportType) != 'undefined')
		{
			criterias.push({'sportType' : queryString.sportType});
		}
		
		var skillLevelCriteria = {};
		if(typeof(queryString.fromSkillLevel) != 'undefined')
		{
			skillLevelCriteria['$gte'] =  parseInt(queryString.fromSkillLevel);
		}
		
		if(typeof(queryString.toSkillLevel) != 'undefined')
		{
			skillLevelCriteria['$lte'] =  parseInt(queryString.toSkillLevel);
		}
		
		if(skillLevelCriteria['$gte'] != null ||
				skillLevelCriteria['$lte'] != null ) {
			criterias.push({'skillLevel' : skillLevelCriteria});
		}
		
		var timeCriteria = {};
		if(typeof(queryString.after) != 'undefined') {
			timeCriteria['$gte'] =  new Date(queryString.after);
		}
		
		if (typeof(queryString.before) != 'undefined') {
			timeCriteria['$lte'] =  new Date(queryString.before);
		}
		
		if(timeCriteria['$gte'] != null ||
				timeCriteria['$lte'] != null ) {
			criterias.push({'startTime' : timeCriteria});
		}
		
		if (criterias.length > 0) {		
			return {'$and' : criterias};
		}
		else {
			return {};
		}
	}
	
	function onCollectionFind(err, documents) {
		var resJson = null;
		
		if (err) {
			console.warn(err);
			resJson = { 'error' : err };
		}
		else {
			resJson = documents;
		}
		
		_db.close();
		
		res.json(resJson);
	}	
	
	MongoClient.connect(connectionString, onMongoConnect);
};

//returns whole event on success, error of failure
exports.getEvent = function(req, res) {
    var _db = null;
	
	function onMongoConnect(err, db) {
		if(err) {
			console.warn(err);
			res.json({ 'error' : err });
		}
		else {
			_db = db;
			db.collection('events').findOne({ '_id' : ObjectID(req.params.eventid) }, onCollectionFindOne);
		}
	}
	
	function onCollectionFindOne(err, document) {
		var resJson = null;
		
		if (err) {
			console.warn(err);
			resJson = { 'error' : err };
		}
		else {
			resJson = document;
		}
		
		_db.close();
		
		res.json(resJson);
	}
	
	MongoClient.connect(connectionString, onMongoConnect);
};

//returns { 'update' : 1 } on success, error of failure
exports.updateEvent = function(req, res) {
    var _db = null;
	function onMongoConnect(err, db) {
		if(err) {
			console.warn(err);
			res.json({ 'error' : err });
		}
		else {
			_db = db;
			var updatedEvent = parseReq(req);
			db.collection('events').update({ '_id' : ObjectID(req.params.eventid) }, 
					{ '$set' : updatedEvent }, {safe: true}, onCollectionUpdate);
		}
	}
	
	function parseReq(req) {
		var updatedEvent = {};
		var fields = ['name', 'sportType', 'skillLevel', 'startTime', 'endTime',
            'minParticipants', 'maxParticipants', 'location'];
		for (var i = 0; i < fields.length; i++) {
			if (typeof(req.body[fields[i]]) != 'undefined') {
				if (fields[i] == 'startTime' || fields[i] == 'endTime') {
					updatedEvent[fields[i]] = new Date(req.body[fields[i]]);
				}
				else {
					updatedEvent[fields[i]] = req.body[fields[i]];
				}
			}
		}
		
		return updatedEvent;
	}
	
	function onCollectionUpdate(err, count) {
		var resJson = null;
		
		if (err) {
			console.warn(err);
			resJson = { 'error' : err };
		}
		else {
			resJson = { 'update' : count };
		}
		
		_db.close();
		
		res.json(resJson);
	}
	
	MongoClient.connect(connectionString, onMongoConnect);
};

//returns { 'delete' : 1 } on success, error of failure
exports.deleteEvent = function(req, res) {
    var _db = null;
	function onMongoConnect(err, db) {
		if(err) {
			console.warn(err);
			res.json({ 'error' : err });
		}
		else {
			_db = db;
			db.collection('events').remove(
				{ '_id' : ObjectID(req.params.eventid) }, onCollectionRemove);
		}
	}
	
	function onCollectionRemove(err, count) {
		var resJson = null;
		
		if (err) {
			console.warn(err);
			resJson = { 'error' : err };
		}
		else {
			resJson = { 'delete' : count };
		}
		
		_db.close();
		
		res.json(resJson);
	}
	
	MongoClient.connect(connectionString, onMongoConnect);
};

//returns { 'update' : 1 } on success, error of failure
exports.addEventMembers = function(req, res) {
    var _db = null;
	function onMongoConnect(err, db) {
		if(err) {
			console.warn(err);
			res.json({ 'error' : err });
		}
		else {
			_db = db;
			db.collection('events').update({ '_id' : ObjectID(req.params.eventid) }, 
					{ '$addToSet' : { 'members' : { '$each' : req.body }}}, {safe: true}, onCollectionUpdate);
		}
	}
	
	function onCollectionUpdate(err, count) {
		var resJson = null;
		
		if (err) {
			console.warn(err);
			resJson = { 'error' : err };
		}
		else {
			resJson = { 'update' : count };
		}
		
		_db.close();
		
		res.json(resJson);
	}
	
	MongoClient.connect(connectionString, onMongoConnect);
};


//returns event members on success, error of failure
exports.getEventMembers = function(req, res) {
    var _db = null;
	function onMongoConnect(err, db) {
		if(err) {
			console.warn(err);
			res.json({ 'error' : err });
		}
		else {
			_db = db;
			db.collection('events').findOne({ '_id' : ObjectID(req.params.eventid) }, 
					{ 'members' : 1, '_id' : 0 }, onCollectionFindOne);
		}
	}
	
	function onCollectionFindOne(err, document) {
		var resJson = null;
		
		if (err) {
			console.warn(err);
			resJson = { 'error' : err };
		}
		else {
			resJson = document;
		}
		
		_db.close();
		
		res.json(resJson);
	}
	
	MongoClient.connect(connectionString, onMongoConnect);
};

//returns { 'update' : 1 } on success, error of failure
exports.removeEventMember = function(req, res) {
    var _db = null;
	function onMongoConnect(err, db) {
		if(err) {
			console.warn(err);
			res.json({ 'error' : err });
		}
		else {
			_db = db;
			db.collection('events').update({ '_id' : ObjectID(req.params.eventid) }, 
					{ '$pull' : { 'members' : req.params.membername }}, {safe: true}, onCollectionUpdate);
		}
	}
	
	function onCollectionUpdate(err, count) {
		var resJson = null;
		
		if (err) {
			console.warn(err);
			resJson = { 'error' : err };
		}
		else {
			resJson = { 'update' : count };
		}
		
		_db.close();
		
		res.json(resJson);
	}
	
	MongoClient.connect(connectionString, onMongoConnect);
};

//returns { 'update' : 1 } on success, error of failure
exports.addEventResults = function(req, res) {
    var _db = null;
	function onMongoConnect(err, db) {
		if(err) {
			console.warn(err);
			res.json({ 'error' : err });
		}
		else {
			_db = db;			
			db.collection('events').update({ '_id' : ObjectID(req.params.eventid) }, 
					{ '$set' : { 'results' : req.body } }, {safe: true}, onCollectionUpdate);
		}
	}
	
	function onCollectionUpdate(err, count) {
		var resJson = null;
		
		if (err) {
			console.warn(err);
			resJson = { 'error' : err };
		}
		else {
			resJson = { 'update' : count };
		}
		
		_db.close();
		
		res.json(resJson);
	}
	
	MongoClient.connect(connectionString, onMongoConnect);	
};

//returns event results on success, error of failure
exports.getEventResults = function(req, res) {
    var _db = null;
	function onMongoConnect(err, db) {
		if(err) {
			console.warn(err);
			res.json({ 'error' : err });
		}
		else {
			_db = db;
			db.collection('events').findOne({ '_id' : ObjectID(req.params.eventid) }, 
					{ 'results' : 1, '_id' : 0 }, onCollectionFindOne);
		}
	}
	
	function onCollectionFindOne(err, document) {
		var resJson = null;
		
		if (err) {
			console.warn(err);
			resJson = { 'error' : err };
		}
		else {
			resJson = document;
		}
		
		_db.close();
		
		res.json(resJson);
	}
	
	MongoClient.connect(connectionString, onMongoConnect);
};

//returns array of events on success, error of failure
exports.getEventsNearLocation = function(req, res) {
    var _db = null;
	function onMongoConnect(err, db) {
		if(err) {
			console.warn(err);
			res.json({ 'error' : err });
		}
		else {
			_db = db;
			
			var limit =  req.query.limit ? parseInt(req.query.limit) : 100;
			db.executeDbCommand({ 'geoNear' : 'events' ,
				'near' : [ parseFloat(req.query.longitude) , parseFloat(req.query.latitude) ],
				'maxDistance' : parseFloat(req.query.maxDistance), 'limit' : limit } , onGeoNearComplete);
		}
	}
	
	function onGeoNearComplete(err, geoNearResult) {
		var resJson = null;
		
		if (err) {
			console.warn(err);
			resJson = { 'error' : err };
		}
		else {
			var nearestEvents = [];
			for (var i = 0; i < geoNearResult.documents[0].results.length; i++){
				nearestEvents.push( { 'distance' : geoNearResult.documents[0].results[i].dis,
					'_id' : geoNearResult.documents[0].results[i].obj._id,
					'name' : geoNearResult.documents[0].results[i].obj.name,
					'location' : geoNearResult.documents[0].results[i].obj.location
				});
			}
			
			resJson = { 'events' : nearestEvents };
		}
		
		_db.close();
		
		res.json(resJson);
	}
	
	function isQueryStringValid(queryString) {
		var isValid = false;
		if (typeof(queryString.longitude) != 'undefined' &&
			typeof(queryString.latitude) != 'undefined' &&
			typeof(queryString.maxDistance) != 'undefined') {
			isValid = true;			
		}
		
		return isValid;
	}
	
	if (isQueryStringValid(req.query)) {
		MongoClient.connect(connectionString, onMongoConnect);
	}
	else {
		res.json({ 'error' : 'must contain "longitude", "latitude" and "maxDistance" in query string.'});
	}
};